#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# @Time: 2024-02-08 17:36

# @Despcription: 定义函数的各种注意事项

#----------------------------------------------------------
# 函数基本定义与返回值
#----------------------------------------------------------
if(1):
    # 函数声明时带注解， 标识出参数以及函数返回值的数据类型
    # 注意， 函数注解是冒号:数据类型形式， 函数返回值类型是箭头->数据类型 形式
    # TODO: 函数注解并不会限制传入的数据类型， 只是起说明作用
    def func_firt(p1: tuple, p2:dict = {"k": "vvv"}, p3:str = "I am p3")->str:
        # 文档字符串， 对函数功能及参数做必要说明
        """函数功能摘要
        
        @param p1 tuple
        @param p2 dictionary
        @param p3 string
        @return None
        """
        print("p1:{}, p2:{}, p3:{}".format(p1, p2, p3))
        return "string value: " + p3

    # 函数没有return 语句时， 返回None
    p1 = list(range(1, 6))
    p2 = {"k1":"v1", "k2":"v2"}
    print(func_firt(p1, p2))
    print()
    # TODO: __doc__输出文档字符串
    print(func_firt.__doc__)
    # TODO: __annotations__输出函数注解
    print(func_firt.__annotations__)

#----------------------------------------------------------
# 函数定义与调用的作用域
#----------------------------------------------------------
# 默认值参数可以使用外部局部变量
# TODO: 函数可以定义在分支结构中
if(0):
    a = 100
    if(1):
        a = 5
        def func_3(a, b=a):
            print(a, b)
        func_3(1)

# TODO: 可以在函数中定义函数
# TODO: 被定义的函数只在当前定义域内有效
if (0):
    def func_in():
        print("outer func_in")
    def func_out():
        print("func_out")
        def func_in_1():
            print("func_in_1")
            def func_in_2():
                print("func_in_2")
                def func_in_3():
                    print("func_in_3")
                func_in_3()
                func_in()

            func_in_2()
        func_in_1()

    func_out()

#----------------------------------------------------------
# 函数名赋值
#----------------------------------------------------------
# 函数名相当于指针， 下面的操作相当于给函数起了别名
# 注意 a = func_first() 时， a 的值为函数的返回值， 不是
if(0):
    a = func_firt
    print(a())

#----------------------------------------------------------
# 默认值参数
#----------------------------------------------------------
def func_second(a, b=1):
    print(a, b)

if(0):
    func_second(1)
    func_second(1, 2)

# 默认值参数的值如果是可变的对象时， 该参数相当于静态变量， 多次调用会结果递增
if (0):
    def func_list(a, b = []):
        b.append(a)
        return b
    func_list(1)
    func_list(2)
    print(func_list(3)) # [1, 2, 3]

    #避免上述现象, 后续调用中不共享默认值参数， 要将默认值定义为None, 或者定义为某个字面量，在后续判断中将其重新定义
    def func_list_1(a, b = None):
        if(b is None):
            b = []
        b.append(a)
        return b
    func_list_1(1)
    func_list_1(2)
    print(func_list_1(3))

    # 字面量值不会结果递增
    def func_num(a, b = "hello"):
        b += a
        return b
    func_num("aaa")
    func_num("bbb")
    print(func_num("ccc")) # helloccc

#----------------------------------------------------------
# 关键字参数
#----------------------------------------------------------
# 关键字参数必须跟在位置参数后面  TODO: 有例外，当所有位置参数也以关键字参数形式赋值时， 位置随意
# 关键字参数不可以多次赋值
if(0):
    def func_transfer(src, dst_home=None, dst_company=None):
        dst = None
        if (dst_home is not None):
            dst = dst_home
        elif (dst_company is not None):
            dst = dst_company
        else:
            dst = "nowhere"
        print("transfer from {} to {}".format(src, dst))
    func_transfer( dst_home = "home", src="US")

#----------------------------------------------------------
# 变参
#----------------------------------------------------------
if(0):
    # 一个*号，接收的所有参数会组成一个元祖， 按照元祖遍历处理处理即可
    # TODO: *号变参只能出现一次
    # TODO: 可变参数后只能跟关键字参数
    def func_var(*args, name):
        for idx, arg in enumerate(args) :
            print("args[{}] = {}".format(idx, arg))
        print("name = {}".format(name))

    func_var("aa", "bb", 1, 2, 99, name="bob")
    print("")

    # 两个*号， 接收的参数是一个字典， 按照字典遍历即可
    # TODO: 两个*号的变参，传参时必须以关键字参数形式传入
    # TODO: 两个*号的变参， 只能出现一次
    def func_var2(**kwargs):
        for key, value in kwargs.items() :
            print("k:{} v:{}".format(key, value))

    func_var2(a=1, b=2, c=3)
    print("")

    # 单双*号可以共存
    def func_var3(*args, **kwargs):
        for idx, arg in enumerate(args) :
            print("args[{}] = {}".format(idx, arg))
        for key, value in kwargs.items() :
            print("k:{} v:{}".format(key, value))

    func_var3("aa", "bb", c=3, d=4)

#----------------------------------------------------------
# 特殊符号参数
#----------------------------------------------------------
if(0):
    # / 前，只能是位置参数
    def func_v(arg1, arg2=3, /):
        print("arg1={}, arg2={}".format(arg1, arg2))

    func_v(1, 100)
    print("")

    # TODO: / 标识符解决关键字参数冲突: 当位置参数的变量名与可变参数的键相同时， 就会冲突
    def func_v1(arg, /, **arg3):
        return 'arg' in arg3
    
    print(func_v1(100, **{"arg":100}))
    print("")

    # * 后, 只能是关键字参数
    def func_v2(*, arg1, arg2):
        print("arg1={}, arg2={}".format(arg1, arg2))
    func_v2( arg2="bbb", arg1="aaa")
    print("")

    # / 和 * 之间可以是传位置参数，也可以传关键字参数
    def func_v3(arg1, arg2, /, arg3, arg4, *, arg5, arg6):
        print("arg1={}, arg2={}, arg3={}, arg4={}, arg5={}, arg6={}".format(arg1, arg2, arg3, arg4, arg5, arg6))
    func_v3(1, 2, "xxx", arg4=5, arg5="ccc", arg6="ddd")

#----------------------------------------------------------
# 参数解包
#----------------------------------------------------------
if(0):
    def f(p):
        # 调用range函数时， 需要独立的参数， 不能直接传元祖进去。所以需要用*号把元祖p展开后传给range()
        return list(range(*p))
    parms = (3, 9)
    print(f(parms))

    def f1(v1, v2="param V2", v3="param V3"):
        print("v1={}, v2={}, v3={}".format(v1, v2, v3))
        return 
    params = {"v1":1, "v2": "v2-value", "v3":"v3-value"}
    # 使用 ** 可以将params解包为三个独立的参数传给f1(), 不加**时， params将作为参数v1传入
    f1(**params)

#----------------------------------------------------------
# Lambda表达式
#----------------------------------------------------------
if(0):
    a = 15 * 3
    b = 10
    x = lambda a, b: a > b
    # TODO: Python 没有三元运算符， 可以用lambda表达式代替
    print("x is {}".format(x(a, b)))

